package org.javaboy.security.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.*;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.security.web.authentication.logout.LogoutSuccessHandler;

import javax.annotation.Resource;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * @Author 江南一点雨  单个security的配置
 * @Site www.javaboy.org 2019-08-11 9:32
 */
@Configuration
public class SecurityConfig extends WebSecurityConfigurerAdapter {
    @Resource
    private ObjectMapper objectMapper;

    @Bean
    PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    // 配置登录的账号
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("javaboy").password("$2a$10$KlbLLUVVyvP0/vuhlabv0upg/ALdCrVEhUJOJDEND5ZtZJ.nGfVCm").roles("admin")
                .and()
                .withUser("江南一点雨").password("$2a$10$KlbLLUVVyvP0/vuhlabv0upg/ALdCrVEhUJOJDEND5ZtZJ.nGfVCm").roles("user");
    }

    @Override
    protected void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests()//开启配置
                .antMatchers("/admin/**").hasRole("admin")//路径符合这个需要admin角色
//                .antMatchers("user/**").hasAnyRole("admin", "user")//路径符合这个，需要这两个的任意一个
                .antMatchers("/user/**").access("hasAnyRole('user','admin')")
                .anyRequest().authenticated()//其他请求，登录后就可以访问
                .and()
                .formLogin()//表单登录
                .loginProcessingUrl("/doLogin")//处理登录请求的地址
                .loginPage("/login")//配置登录页面（实际上还是一个请求地址） 前后端分类不存在这种页面 这里还是访问的应该请求，会根据这请求去返回登录页面
                .usernameParameter("uname")//修改登录的key
                .passwordParameter("passwd")//修改登录的key
                .successHandler(new AuthenticationSuccessHandler() { //登录成功的处理
                    @Override// authentication保存了登录成功后的信息
                    public void onAuthenticationSuccess(HttpServletRequest req, HttpServletResponse resp,
                                                        Authentication authentication) throws IOException, ServletException {
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        Map<String, Object> map = new HashMap<>();
                        map.put("status", 200);
                        map.put("msg", authentication.getPrincipal());
                        out.write(new ObjectMapper().writeValueAsString(map));
                        out.flush();
                        out.close();
                    }
                })
                .failureHandler(new AuthenticationFailureHandler() {//登录失败的处理
                    @Override // e登录失败的异常
                    public void onAuthenticationFailure(HttpServletRequest req, HttpServletResponse resp,
                                                        AuthenticationException e) throws IOException, ServletException {
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        Map<String, Object> map = new HashMap<>();
                        map.put("status", 401);
                        if (e instanceof LockedException) {
                            map.put("msg", "账户被锁定，登录失败!");
                        } else if (e instanceof BadCredentialsException) {
                            map.put("msg", "用户名或密码输入错误，登录失败!");
                        } else if (e instanceof DisabledException) {
                            map.put("msg", "账户被禁用，登录失败!");
                        } else if (e instanceof AccountExpiredException) {
                            map.put("msg", "账户过期，登录失败!");
                        } else if (e instanceof CredentialsExpiredException) {
                            map.put("msg", "密码过期，登录失败!");
                        } else {
                            map.put("msg", "登录失败!");
                        }
                        out.write(new ObjectMapper().writeValueAsString(map));
                        out.flush();
                        out.close();
                    }
                })
                .permitAll()
                .and()
                .logout()
                .logoutUrl("/logout") //配置注销请求的地址
                .logoutSuccessHandler(new LogoutSuccessHandler() {//注销成功后的回调
                    @Override
                    public void onLogoutSuccess(HttpServletRequest req, HttpServletResponse resp,
                                                Authentication authentication) throws IOException, ServletException {
                        resp.setContentType("application/json;charset=utf-8");
                        PrintWriter out = resp.getWriter();
                        Map<String, Object> map = new HashMap<>();
                        map.put("status", 200);
                        map.put("msg", "注销登录成功!");
                        out.write(new ObjectMapper().writeValueAsString(map));
                        out.flush();
                        out.close();
                    }
                })
                .and()
                .csrf().disable();

        /**
         * 权限不足处理
         */
        http.exceptionHandling().accessDeniedHandler(new AccessDeniedHandler() {
            @Override
            public void handle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AccessDeniedException e) throws IOException {

                httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);

                Map<String, Object> failureMap = new HashMap<>();
                failureMap.put("code", 403);
                failureMap.put("msg", "权限不足！");

                httpServletResponse.getWriter().println(objectMapper.writeValueAsString(failureMap));

            }
        });
//        /**
//         * 未登陆处理
//         */
//        http.exceptionHandling().authenticationEntryPoint(new AuthenticationEntryPoint() {
//            @Override
//            public void commence(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException {
//
//
//                httpServletResponse.setContentType(MediaType.APPLICATION_JSON_UTF8_VALUE);
//
//                Map<String, Object> failureMap = new HashMap<>();
//                failureMap.put("code", 401);
//                failureMap.put("msg", "请先登录！");
//
//                httpServletResponse.getWriter().println(objectMapper.writeValueAsString(failureMap));
//
//            }
//        });
//
    }
}
